import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler


# 14　自定义Sigmoid函数,并画出其图像  4分
def model(X, theta):
    return X.dot(theta)


def sigmoid(z):
    return 1 / (1 + np.exp(-z))


plt.figure(figsize=[15, 5])
spr = 1  # subplot row
spc = 3  # subplot column
spn = 1  # subplot number
plt.subplot(spr, spc, spn)
plt_x = np.linspace(-10, 10, 1001)
plt_y = sigmoid(plt_x)
plt.plot(plt_x, plt_y, label='sigmoid')
plt.grid()
plt.legend()


# 15　实现上述模型的costFunction，并加入正则化  4分
def costFunction(h, y, theta, lam):
    m = len(h)
    theta_cp = theta.copy()
    theta_cp[0] = 0
    r = lam / m * np.sum(theta_cp ** 2)
    j = - 1 / m * np.sum(y * np.log(h) + (1 - y) * np.log(1 - h))
    return j + r


# 16　自定义梯度下降函数  4分
# 要求lamda=0.1,alpha=0.01,iter_num=15000
def grad(X, y, lam=0.1, alpha=0.01, iter_num=15000):
    m, n = X.shape
    group = iter_num // 20
    theta = np.zeros(n)
    j_his = np.zeros(iter_num)  # cost function values history
    for i in range(iter_num):
        z = model(X, theta)
        h = sigmoid(z)
        j = costFunction(h, y, theta, lam)
        j_his[i] = j
        if 0 == i % group:
            print(f'#{i + 1} cost function value = {j}')
        theta_cp = theta.copy()
        theta_cp[0] = 0
        r = lam / m * theta_cp
        dt = 1 / m * X.T.dot(h - y)
        dt += r
        theta -= alpha * dt
    if 0 != i % group:
        print(f'#{i + 1} cost function value = {j}')
    return theta, j_his, h


# 17　自定义计算模型评估指标的方法  要求画出迭代过程中代价值随迭代次数变化的曲线图  4分
def score(h, y):
    return np.mean(y == (h > 0.5))


# load and scale
scaler = StandardScaler()
dt_train = np.loadtxt(r'../../../../../large_data/finalX/train.txt', delimiter=',')
m_train = len(dt_train)
x_train = dt_train[:, :-1]
x_train = scaler.fit_transform(x_train)
X_train = np.c_[np.ones(m_train), x_train]
y_train = dt_train[:, -1]
dt_test = np.loadtxt(r'../../../../../large_data/finalX/test.txt', delimiter=',')
m_test = len(dt_test)
x_test = dt_test[:, :-1]
x_test = scaler.fit_transform(x_test)
X_test = np.c_[np.ones(m_test), x_test]
y_test = dt_test[:, -1]
# train
theta, j_his, h_train = grad(X_train, y_train)
print(f'Theta = {theta}')
print(f'Training score = {score(h_train, y_train)}')
# plot cost function values
spn += 1
plt.subplot(spr, spc, spn)
plt.plot(j_his, label='cost function')
plt.xlabel('Iterations')
plt.grid()
plt.legend()

# 18　调用自定义的方法用所得模型对测试集的数据进行预测，并输出准确率  4分
z_test = model(X_test, theta)
h_test = sigmoid(z_test)
print(f'Testing score = {score(h_test, y_test)}')

# 19　使用密度和体积两组特征画出训练集上的0-1分布图  4分
spn += 1
plt.subplot(spr, spc, spn)
plt.scatter(x_train[:, 1], x_train[:, 2], s=1, c=y_train)
plt.grid()
plt.xlabel('density')
plt.ylabel('volume')

# show all drawings
plt.show()
